package com.onlyxiahui.common.action.description.handler.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.onlyxiahui.common.action.description.DocumentContext;
import com.onlyxiahui.common.action.description.annotation.DocModule;
import com.onlyxiahui.common.action.description.annotation.DocTitle;
import com.onlyxiahui.common.action.description.bean.MethodData;
import com.onlyxiahui.common.action.description.bean.ModuleData;
import com.onlyxiahui.common.action.description.bean.ParameterData;
import com.onlyxiahui.common.action.description.common.TagConstant;
import com.onlyxiahui.common.action.description.enums.type.ContentType;
import com.onlyxiahui.common.action.description.handler.MethodHandler;
import com.onlyxiahui.common.action.description.handler.ModuleHandler;
import com.onlyxiahui.common.action.description.handler.ModuleTitleHandler;
import com.onlyxiahui.common.action.description.util.ActionParameterUtil;
import com.onlyxiahui.common.action.description.util.ActionCommentUtil;
import com.onlyxiahui.common.action.description.util.JavaTagUtil;
import com.onlyxiahui.common.action.description.util.ActionTitleUtil;
import com.thoughtworks.qdox.model.JavaClass;

/**
 * Description <br>
 * Date 2020-01-07 15:40:19<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public abstract class BaseModuleHandler implements ModuleHandler {

	protected ModuleData binding(DocumentContext context, Class<?> classType, List<String> paths) {
		ModuleData md = create(context, classType, paths);
		List<MethodData> methodList = build(context, md, classType);
		boolean hasMethodList = !methodList.isEmpty();
		if (hasMethodList) {
			md.setMethods(methodList);
		}
		return hasMethodList ? md : null;
	}

	protected List<MethodData> build(DocumentContext context, ModuleData moduleData, Class<?> moduleClass) {
		List<MethodData> methodList = new ArrayList<>();
		Method[] methods = moduleClass.getMethods();
		if (null != methods && methods.length > 0) {
			for (Method method : methods) {
				MethodData methodData = build(context, moduleData, moduleClass, method);
				if (null != methodData) {
					methodList.add(methodData);
					setParameterTypes(methodData, methodData.getParameters());
				}
			}
		}
		return methodList;
	}

	protected MethodData build(DocumentContext context, ModuleData moduleData, Class<?> moduleClass, Method method) {
		for (MethodHandler mh : context.getMethodHandlers()) {
			if (mh.support(context, moduleData, method)) {
				return mh.handle(context, moduleData, method);
			}
		}
		return null;
	}

	public void setParameterTypes(MethodData md, List<ParameterData> parameters) {
		if (null != md && null != parameters) {
			Set<String> parameterSet = new HashSet<>();
			for (ParameterData p : parameters) {
				String type = p.getParameterType();
				parameterSet.add(type);
			}
			Set<String> contentTypeSet = new HashSet<>();
			for (String p : parameterSet) {
				if (ActionParameterUtil.isBody(p)) {
					contentTypeSet.add(ContentType.ApplicationJson.type());
				} else if (ActionParameterUtil.isNode(p)) {
					contentTypeSet.add(ContentType.ApplicationJson.type());
				} else if (ActionParameterUtil.isItem(p)) {
					contentTypeSet.add(ContentType.ApplicationFormUrlencoded.type());
				}
			}

			for (String p : parameterSet) {
				md.addParameterType(p);
			}
			for (String p : contentTypeSet) {
				md.addContentType(p);
			}
		}
	}

	public ModuleData create(DocumentContext context, Class<?> moduleClass, List<String> paths) {
		String className = moduleClass.getName();

		String key = getKey(context, moduleClass);
		String superKey = getSuperKey(context, moduleClass);
		// String action = "";
		String title = getTitle(context, moduleClass);
		String description = "";
		boolean ignore = context.getIgnoreJudgeBox().ignore(context, moduleClass);

		ModuleData md = new ModuleData();
		JavaClass jc = context.getSourceBox().getJavaClassByName(className);
		if (null != jc) {
			description = jc.getComment();
		}

		if (null == description) {
			description = "";
		}

		md.setKey(key);
		md.setSuperKey(superKey);
		md.setPaths(paths);
		md.setTitle(title);
		md.setDescription(description);
		md.setIgnore(ignore);
		return md;
	}

	public String getKey(DocumentContext context, Class<?> moduleClass) {
		String className = moduleClass.getName();
		String key = null;
		JavaClass jc = context.getSourceBox().getJavaClassByName(className);
		DocModule dmd = moduleClass.getAnnotation(DocModule.class);
		if (null != dmd) {
			key = dmd.key();
		}
		if (null == key || key.isEmpty()) {
			if (null != jc) {
				key = JavaTagUtil.getTagValueByName(jc, TagConstant.DOC_MODULE_KEY);
			}
		}
		if (null == key || key.isEmpty()) {
			key = className;
		}
		return null == key ? className : key;
	}

	public String getSuperKey(DocumentContext context, Class<?> moduleClass) {
		String className = moduleClass.getName();
		String superKey = null;
		JavaClass jc = context.getSourceBox().getJavaClassByName(className);
		DocModule dmd = moduleClass.getAnnotation(DocModule.class);
		if (null != dmd) {
			superKey = dmd.superKey();
		}
		if (null == superKey || superKey.isEmpty()) {
			if (null != jc) {
				superKey = JavaTagUtil.getTagValueByName(jc, TagConstant.DOC_MODULE_SUPER_KEY);
			}
		}
		return superKey;
	}

	public String getTitle(DocumentContext context, Class<?> moduleClass) {
		String className = moduleClass.getName();
		String classSimpleName = moduleClass.getSimpleName();

		String title = null;
		JavaClass jc = context.getSourceBox().getJavaClassByName(className);
		DocModule dmd = moduleClass.getAnnotation(DocModule.class);
		DocTitle docTitle = moduleClass.getAnnotation(DocTitle.class);

		String comment = "";
		if (null != jc) {
			comment = jc.getComment();
		}

		for (ModuleTitleHandler h : context.getModuleTitleHandlers()) {
			if ((null == title || title.isEmpty()) && h.support(context, moduleClass, comment)) {
				title = h.handle(context, moduleClass, comment);
			}
		}

		if (null == title || title.isEmpty()) {
			if (null != dmd) {
				title = dmd.title();
			}
		}

		if (null == title || title.isEmpty()) {
			if (null != docTitle) {
				title = docTitle.value();
			}
		}

		if (null == title || title.isEmpty()) {
			if (null != comment && !comment.isEmpty()) {
				title = ActionCommentUtil.getFirstCommentLine(comment);
			}
		}

		if (null == title || title.isEmpty()) {
			title = classSimpleName;
		} else {
			title = ActionTitleUtil.handle(title);
		}
		return null == title ? "" : title;
	}
}
